/* -*- Mode: C; c-basic-offset:4 ; -*- */
/*
 *  (C) 2001 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */
#include <stdio.h>
#include "mpi.h"
#include "mpitest.h"

#define MAX_BUF_SIZE_LG 22
#define NUM_MSGS_PER_BUF_SIZE 5
char buf[1 << MAX_BUF_SIZE_LG];

/* 
 * This program verifies that MPI_Probe() is operating properly in the face of
 * unexpected messages arriving after MPI_Probe() has
 * been called.  This program may hang if MPI_Probe() does not return when the
 * message finally arrives (see req #375).
 */
int main(int argc, char **argv)
{
    int p_size;
    int p_rank;
    int msg_size_lg;
    int errs = 0;
    int mpi_errno;
    
    MTest_Init(&argc, &argv);

    MPI_Comm_size(MPI_COMM_WORLD, &p_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &p_rank);
    /* To improve reporting of problems about operations, we
       change the error handler to errors return */
    MPI_Comm_set_errhandler( MPI_COMM_WORLD, MPI_ERRORS_RETURN );


    for (msg_size_lg = 0; msg_size_lg <= MAX_BUF_SIZE_LG; msg_size_lg++)
    {
	const int msg_size = 1 << msg_size_lg;
	int msg_cnt;

	MTestPrintfMsg( 2, "testing messages of size %d\n", msg_size );
	for (msg_cnt = 0; msg_cnt < NUM_MSGS_PER_BUF_SIZE; msg_cnt++)
        {
	    MPI_Status status;
	    const int tag = msg_size_lg * NUM_MSGS_PER_BUF_SIZE + msg_cnt;
	    
	    MTestPrintfMsg( 2, "Message count %d\n", msg_cnt );
	    if (p_rank == 0)
	    {
		int p;
		
		for (p = 1; p < p_size; p ++)
		{
		    /* Wait for synchronization message */
		    mpi_errno = MPI_Recv(NULL, 0, MPI_BYTE, MPI_ANY_SOURCE, 
					 tag, MPI_COMM_WORLD, &status);
		    if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		    {
			MTestPrintError(mpi_errno);
		    }
		    
		    if (status.MPI_TAG != tag && errs++ < 10)
		    {
			printf("ERROR: unexpected message tag from MPI_Recv(): lp=0, rp=%d, expected=%d, actual=%d, count=%d\n",
			       status.MPI_SOURCE, status.MPI_TAG, tag, msg_cnt);
		    }

#		    if defined(VERBOSE)
		    {
			printf("sending message: p=%d s=%d c=%d\n", 
			       status.MPI_SOURCE, msg_size, msg_cnt);
		    }
#		    endif
		    
		    /* Send unexpected message which hopefully MPI_Probe() is 
		       already waiting for at the remote process */
		    mpi_errno = MPI_Send (buf, msg_size, MPI_BYTE, 
			    status.MPI_SOURCE, status.MPI_TAG, MPI_COMM_WORLD);
		    if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		    {
			MTestPrintError(mpi_errno);
		    }
		}
	    }
	    else
	    {
		int incoming_msg_size;

		/* Send synchronization message */
		mpi_errno = MPI_Send(NULL, 0, MPI_BYTE, 0, tag, MPI_COMM_WORLD);
		if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		{
		    MTestPrintError(mpi_errno);
		}

		/* Perform probe, hopefully before the master process can 
		   send its reply */
		mpi_errno = MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, 
				      MPI_COMM_WORLD, &status);
		if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		{
		    MTestPrintError(mpi_errno);
		}
		mpi_errno = MPI_Get_count(&status, MPI_BYTE, &incoming_msg_size);
		if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		{
		    MTestPrintError(mpi_errno);
		}
		if (status.MPI_SOURCE != 0 && errs++ < 10)
		{
		    printf("ERROR: unexpected message source from MPI_Probe(): p=%d, expected=0, actual=%d, count=%d\n",
			   p_rank, status.MPI_SOURCE, msg_cnt);
		}
		if (status.MPI_TAG != tag && errs++ < 10)
		{
		    printf("ERROR: unexpected message tag from MPI_Probe(): p=%d, expected=%d, actual=%d, count=%d\n",
			   p_rank, tag, status.MPI_TAG, msg_cnt);
		}
		if (incoming_msg_size != msg_size && errs++ < 10)
		{
		    printf("ERROR: unexpected message size from MPI_Probe(): p=%d, expected=%d, actual=%d, count=%d\n",
			   p_rank, msg_size, incoming_msg_size, msg_cnt);
		}

		/* Receive the probed message from the master process */
		mpi_errno = MPI_Recv(buf, msg_size, MPI_BYTE, 0, tag, 
				     MPI_COMM_WORLD, &status);
		if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		{
		    MTestPrintError(mpi_errno);
		}
		mpi_errno = MPI_Get_count(&status, MPI_BYTE, &incoming_msg_size);
		if (mpi_errno != MPI_SUCCESS && errs++ < 10)
		{
		    MTestPrintError(mpi_errno);
		}
		if (status.MPI_SOURCE != 0 && errs++ < 10)
		{
		    printf("ERROR: unexpected message source from MPI_Recv(): p=%d, expected=0, actual=%d, count=%d\n",
			   p_rank, status.MPI_SOURCE, msg_cnt);
		}
		if (status.MPI_TAG != tag && errs++ < 10)
		{
		    printf("ERROR: unexpected message tag from MPI_Recv(): p=%d, expected=%d, actual=%d, count=%d\n",
			   p_rank, tag, status.MPI_TAG, msg_cnt);
		}
		if (incoming_msg_size != msg_size && errs++ < 10)
		{
		    printf("ERROR: unexpected message size from MPI_Recv(): p=%d, expected=%d, actual=%d, count=%d\n",
			   p_rank, msg_size, incoming_msg_size, msg_cnt);
		}
	    }
	}
    }

    MTest_Finalize( errs );
    MPI_Finalize();
    return 0;
}
